/*
 * RMTransactionManager.java   0.0.1    9 Nov 2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.transactions.control.impl;

import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.mentalsmash.tazio.transactions.control.CommitmentAction;
import org.mentalsmash.tazio.transactions.control.SystemTransactionManager;
import org.mentalsmash.tazio.transactions.control.TransactionStateException;
import org.mentalsmash.tazio.transactions.control.TransactionsLog;
import org.mentalsmash.tazio.transactions.control.ValidationAgent;
import org.mentalsmash.tazio.transactions.model.RootSet;
import org.mentalsmash.tazio.transactions.user.RollbackException;
import org.mentalsmash.tazio.transactions.user.Transaction;
import org.mentalsmash.tazio.transactions.user.TransactionStatus;
import org.mentalsmash.tazio.transactions.user.TransactionalSystemException;

import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * @author Andrea Reale
 * @version 0.0.1
 */
@Singleton
class RMTransactionManager implements SystemTransactionManager {

    // instance of TazioTransaction local to a thread
    private final static ThreadLocal<TazioTransactionImpl> currentTransaction = new ThreadLocal<TazioTransactionImpl>();

    private final AtomicInteger seqNoCounter = new AtomicInteger(0);

    private final TransactionsLog transactionsLog;

    private final RootSet rootSet;

    private final ValidationAgent validationAgent;

    // One thread at a time may be in the commitment section. The lock ensures
    // mutual exclusion of threds
    // while the condition variable provides a FIFO ordered queue for threads to
    // await;
    private final Lock commitmentLock = new ReentrantLock();
    private boolean commitmentSectionFree = true;
    private final Condition commitmentSectionCondition = this.commitmentLock
	    .newCondition();

    @Inject
    private RMTransactionManager(final RootSet rs, final TransactionsLog txLog,
	    final ValidationAgent vAgent) {
	this.rootSet = rs;
	this.transactionsLog = txLog;
	this.validationAgent = vAgent;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mentalsmash.tazio.transactions.SystemTransactionManager#commit()
     */
    @Override
    public void commit() throws RollbackException, TransactionStateException,
	    TransactionalSystemException {

	final CommitmentAction action = new RMCommitmentAction(
		(TazioTransactionImpl) getTransaction(), this, this.rootSet);

	try {
	    action.call();
	} catch (final TransactionStateException e) {
	    throw e;
	} catch (final RollbackException e) {
	    throw e;
	} catch (final Exception e) {
	    throw new TransactionalSystemException(e);
	}

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.SystemTransactionManager#commitAsync()
     */
    @Override
    public Future<TransactionStatus> commitAsync()
	    throws TransactionStateException {

	throw new UnsupportedOperationException(
		"This operation does not make sense on RM");
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.SystemTransactionManager#getStatus()
     */
    @Override
    public TransactionStatus getStatus() {
	final Transaction tx = getTransaction();
	return tx.getStatus();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.SystemTransactionManager#getTransaction
     * ()
     */
    @Override
    public Transaction getTransaction() {
	final TazioTransactionImpl tx = currentTransaction.get();
	if (tx == null) {
	    return TazioTransactionImpl.NULL_TRANSACTION;
	} else {
	    return tx;
	}
    }

    /*
     * (non-Javadoc)
     * 
     * @seeorg.mentalsmash.tazio.transactions.SystemTransactionManager#
     * getTransactionsLog()
     */
    @Override
    public TransactionsLog getTransactionsLog() {
	return this.transactionsLog;
    }

    /*
     * (non-Javadoc)
     * 
     * @seeorg.mentalsmash.tazio.transactions.SystemTransactionManager#
     * getValidationAgent()
     */
    @Override
    public ValidationAgent getValidationAgent() {
	return this.validationAgent;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.SystemTransactionManager#rollback()
     */
    @Override
    public void rollback() throws TransactionStateException {
	final TazioTransactionImpl tx = (TazioTransactionImpl) this
		.getTransaction();
	tx.stateTransition(TransactionStatus.ROLLED_BACK);

    }

    protected boolean isCommitmentSectionFree() {
	return this.commitmentSectionFree;
    }

    protected void setCommitmentSectionFree(final boolean commitmentSectionFree) {
	this.commitmentSectionFree = commitmentSectionFree;
    }

    protected Lock getCommitmentLock() {
	return this.commitmentLock;
    }

    protected Condition getCommitmentSectionCondition() {
	return this.commitmentSectionCondition;
    }

    protected int incrementAndGetSeqNoCtr() {
	return this.seqNoCounter.incrementAndGet();
    }

    public int getSeqNoCtr() {
	return this.seqNoCounter.get();
    }

}
